﻿using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using PioneerBoilerplate.Repository.Repositories;
using PioneerBoilerplate.Repository.UnitOfWorks;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;

namespace PioneerBoilerplate.Repository
{
    public class BaseRepository<TDbContext, TEntity> : IBaseRepository<TDbContext, TEntity> where TEntity : class where TDbContext : DbContext
    {
        private readonly IUnitOfWorkManager _unitOfWorkManager;

        public BaseRepository(IUnitOfWorkManager unitOfWorkManager)
        {
            _unitOfWorkManager = unitOfWorkManager;
        }

        public virtual TDbContext CurrentDbContext => AsyncLocalCurrentUnitOfWorkHandleProvider<TDbContext>.Current?.GetActiveUnitOfWork();

        public IUnitOfWorkManager UnitOfWorkManager => _unitOfWorkManager;

        public IUnitOfWorkHandle<TDbContext> BeginUnitOfWork()
        {
            return _unitOfWorkManager.Begin<TDbContext>();
        }

        public IUnitOfWorkHandle<TDbContext> BeginNewUnitOfWork()
        {
            return _unitOfWorkManager.BeginNew<TDbContext>();
        }

        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return uowHander.GetActiveUnitOfWork().Set<TEntity>().FirstOrDefault(predicate);
        }

        public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return await uowHander.GetActiveUnitOfWork().Set<TEntity>().FirstOrDefaultAsync(predicate);
        }

        //public IQueryable<TEntity> GetAll()
        //{
        //    var context = CurrentDbContext;
        //    if (context != null)
        //    {
        //        return context.Set<TEntity>().AsQueryable();
        //    }
        //    throw new ArgumentNullException(nameof(CurrentDbContext));
        //}

        public IQueryable<TEntity> GetAll()
        {
            var context = _unitOfWorkManager.Begin<TDbContext>();
            if (context != null)
            {
                return context.GetActiveUnitOfWork().Set<TEntity>().AsQueryable();
            }
            throw new ArgumentNullException(nameof(CurrentDbContext));
        }

        //public IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
        //{
        //    var context = CurrentDbContext;
        //    if (context != null)
        //    {
        //        var query = context.Set<TEntity>().AsQueryable();
        //        if (propertySelectors != null)
        //        {
        //            foreach (var propertySelector in propertySelectors)
        //            {
        //                query = query.Include(propertySelector);
        //            }
        //        }
        //        return query;
        //    }
        //    throw new ArgumentNullException(nameof(CurrentDbContext));
        //}

        public IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
        {
            var context = _unitOfWorkManager.Begin<TDbContext>();
            if (context != null)
            {
                var query = context.GetActiveUnitOfWork().Set<TEntity>().AsQueryable();
                if (propertySelectors != null)
                {
                    foreach (var propertySelector in propertySelectors)
                    {
                        query = query.Include(propertySelector);
                    }
                }
                return query;
            }
            throw new ArgumentNullException(nameof(CurrentDbContext));
        }

        public List<TEntity> GetAllList()
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return uowHander.GetActiveUnitOfWork().Set<TEntity>().ToList();

        }

        public List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return uowHander.GetActiveUnitOfWork().Set<TEntity>().Where(predicate).ToList();
        }

        public async Task<List<TEntity>> GetAllListAsync()
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return await uowHander.GetActiveUnitOfWork().Set<TEntity>().ToListAsync();
        }

        public async Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return await uowHander.GetActiveUnitOfWork().Set<TEntity>().Where(predicate).ToListAsync();
        }

        public T Query<T>(Func<IQueryable<TEntity>, T> queryMethod)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return queryMethod(GetAll());
        }

        public TEntity Single(Expression<Func<TEntity, bool>> predicate)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return uowHander.GetActiveUnitOfWork().Set<TEntity>().SingleOrDefault(predicate);
        }

        public async Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            return await uowHander.GetActiveUnitOfWork().Set<TEntity>().SingleOrDefaultAsync(predicate);
        }

        public TEntity Insert(TEntity entity)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            entity = uowHander.GetActiveUnitOfWork().Set<TEntity>().Add(entity).Entity;

            uowHander.SaveChange();
            return entity;
        }

        public async Task<TEntity> InsertAsync(TEntity entity)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            entity = uowHander.GetActiveUnitOfWork().Set<TEntity>().Add(entity).Entity;

            await uowHander.SaveChangeAsync();
            return entity;
        }

        public int Inserts(TEntity[] entitys) 
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            uowHander.GetActiveUnitOfWork().Set<TEntity>().AddRange(entitys);
            return uowHander.SaveChange();
        }

        public async Task<int> InsertsAsync(TEntity[] entitys) 
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            await uowHander.GetActiveUnitOfWork().Set<TEntity>().AddRangeAsync(entitys);

            return await uowHander.SaveChangeAsync();
        }

        public TEntity Update(TEntity entity)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            var context = uowHander.GetActiveUnitOfWork();
            AttachIfNot(context, entity);
            context.Entry(entity).State = EntityState.Modified;

            uowHander.SaveChange();
            return entity;
        }

        public async Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            EntityEntry<TEntity> trackedEntity = uowHander.GetActiveUnitOfWork().ChangeTracker.Entries<TEntity>().FirstOrDefault(x => x.Entity == entity);

            if (trackedEntity == null)
            {
                IEntityType entityType = uowHander.GetActiveUnitOfWork().Model.FindEntityType(typeof(TEntity));

                if (entityType == null)
                {
                    throw new InvalidOperationException($"{typeof(TEntity).Name} is not part of EF Core DbContext model");
                }

                string primaryKeyName = entityType.FindPrimaryKey().Properties.Select(p => p.Name).FirstOrDefault();

                if (primaryKeyName != null)
                {
                    Type primaryKeyType = entityType.FindPrimaryKey().Properties.Select(p => p.ClrType).FirstOrDefault();

                    object primaryKeyDefaultValue = primaryKeyType.IsValueType ? Activator.CreateInstance(primaryKeyType) : null;

                    object primaryValue = entity.GetType().GetProperty(primaryKeyName).GetValue(entity, null);

                    if (primaryKeyDefaultValue.Equals(primaryValue))
                    {
                        throw new InvalidOperationException("The primary key value of the entity to be updated is not valid.");
                    }
                }

                uowHander.GetActiveUnitOfWork().Set<TEntity>().Update(entity);
            }

            await uowHander.GetActiveUnitOfWork().SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            return entity;
        }

        public async Task UpdateAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            if (entities == null)
            {
                throw new ArgumentNullException(nameof(entities));
            }

            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            var context = uowHander.GetActiveUnitOfWork();

            context.Set<TEntity>().UpdateRange(entities);
            await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }

        public void Delete(TEntity entity)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            var context = uowHander.GetActiveUnitOfWork();
            AttachIfNot(context, entity);
            context.Set<TEntity>().Remove(entity);

            uowHander.SaveChange();
        }

        public async Task DeleteAsync(TEntity entity)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            var context = uowHander.GetActiveUnitOfWork();
            AttachIfNot(context, entity);
            context.Set<TEntity>().Remove(entity);

            await uowHander.SaveChangeAsync();
        }

        protected virtual void AttachIfNot(DbContext dbContext, TEntity entity)
        {
            var entry = dbContext.ChangeTracker.Entries().FirstOrDefault(ent => ent.Entity == entity);
            if (entry != null)
            {
                return;
            }
            dbContext.Set<TEntity>().Attach(entity);
        }

        public async Task<PaginatedList<TEntity>> GetListAsync(
            PaginationSpecification<TEntity> specification,
            CancellationToken cancellationToken = default)
        {
            if (specification == null)
            {
                throw new ArgumentNullException(nameof(specification));
            }

            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();

            var context = uowHander.GetActiveUnitOfWork();

            PaginatedList<TEntity> paginatedList = await context.Set<TEntity>().ToPaginatedListAsync(specification, cancellationToken);
            return paginatedList;
        }

        /*****************************************************************************************************/
        public async Task<PaginatedList<TProjectedType>> GetListAsync<TProjectedType>(
            PaginationSpecification<TEntity> specification,
            Expression<Func<TEntity, TProjectedType>> selectExpression,
            CancellationToken cancellationToken = default)
            where TProjectedType : class
        {
            if (specification == null)
            {
                throw new ArgumentNullException(nameof(specification));
            }

            if (selectExpression == null)
            {
                throw new ArgumentNullException(nameof(selectExpression));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>().GetSpecifiedQuery((SpecificationBase<TEntity>)specification);

            PaginatedList<TProjectedType> paginatedList = await query.Select(selectExpression)
                .ToPaginatedListAsync(specification.PageIndex, specification.PageSize, cancellationToken);
            return paginatedList;
        }

        public Task<TEntity> GetByIdAsync(object id, CancellationToken cancellationToken = default)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            return GetByIdAsync(id, false, cancellationToken);
        }

        public Task<TEntity> GetByIdAsync(object id, bool asNoTracking, CancellationToken cancellationToken = default)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            return GetByIdAsync(id, null, asNoTracking, cancellationToken);
        }

        public Task<TEntity> GetByIdAsync(
            object id,
            Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> includes,
            CancellationToken cancellationToken = default)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            return GetByIdAsync(id, includes, false, cancellationToken);
        }

        public async Task<TEntity> GetByIdAsync(
            object id,
            Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> includes,
            bool asNoTracking = false,
            CancellationToken cancellationToken = default)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IEntityType entityType = uowHander.GetActiveUnitOfWork().Model.FindEntityType(typeof(TEntity));

            string primaryKeyName = entityType.FindPrimaryKey().Properties.Select(p => p.Name).FirstOrDefault();
            Type primaryKeyType = entityType.FindPrimaryKey().Properties.Select(p => p.ClrType).FirstOrDefault();

            if (primaryKeyName == null || primaryKeyType == null)
            {
                throw new ArgumentException("Entity does not have any primary key defined", nameof(id));
            }

            object primayKeyValue = null;

            try
            {
                primayKeyValue = Convert.ChangeType(id, primaryKeyType, CultureInfo.InvariantCulture);
            }
            catch (Exception)
            {
                throw new ArgumentException($"You can not assign a value of type {id.GetType()} to a property of type {primaryKeyType}");
            }

            ParameterExpression pe = Expression.Parameter(typeof(TEntity), "entity");
            MemberExpression me = Expression.Property(pe, primaryKeyName);
            ConstantExpression constant = Expression.Constant(primayKeyValue, primaryKeyType);
            BinaryExpression body = Expression.Equal(me, constant);
            Expression<Func<TEntity, bool>> expressionTree = Expression.Lambda<Func<TEntity, bool>>(body, new[] { pe });

            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (includes != null)
            {
                query = includes(query);
            }

            if (asNoTracking)
            {
                query = query.AsNoTracking();
            }

            TEntity enity = await query.FirstOrDefaultAsync(expressionTree, cancellationToken).ConfigureAwait(false);
            return enity;
        }

        public async Task<TProjectedType> GetByIdAsync<TProjectedType>(
            object id,
            Expression<Func<TEntity, TProjectedType>> selectExpression,
            CancellationToken cancellationToken = default)
        {
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            if (selectExpression == null)
            {
                throw new ArgumentNullException(nameof(selectExpression));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IEntityType entityType = uowHander.GetActiveUnitOfWork().Model.FindEntityType(typeof(TEntity));

            string primaryKeyName = entityType.FindPrimaryKey().Properties.Select(p => p.Name).FirstOrDefault();
            Type primaryKeyType = entityType.FindPrimaryKey().Properties.Select(p => p.ClrType).FirstOrDefault();

            if (primaryKeyName == null || primaryKeyType == null)
            {
                throw new ArgumentException("Entity does not have any primary key defined", nameof(id));
            }

            object primayKeyValue = null;

            try
            {
                primayKeyValue = Convert.ChangeType(id, primaryKeyType, CultureInfo.InvariantCulture);
            }
            catch (Exception)
            {
                throw new ArgumentException($"You can not assign a value of type {id.GetType()} to a property of type {primaryKeyType}");
            }

            ParameterExpression pe = Expression.Parameter(typeof(TEntity), "entity");
            MemberExpression me = Expression.Property(pe, primaryKeyName);
            ConstantExpression constant = Expression.Constant(primayKeyValue, primaryKeyType);
            BinaryExpression body = Expression.Equal(me, constant);
            Expression<Func<TEntity, bool>> expressionTree = Expression.Lambda<Func<TEntity, bool>>(body, new[] { pe });

            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            return await query.Where(expressionTree).Select(selectExpression).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
        }

        public Task<TEntity> GetAsync(
            Expression<Func<TEntity, bool>> condition,
            CancellationToken cancellationToken = default)
        {
            return GetAsync(condition, null, false, cancellationToken);
        }

        public Task<TEntity> GetAsync(
            Expression<Func<TEntity, bool>> condition,
            bool asNoTracking,
            CancellationToken cancellationToken = default)
        {
            return GetAsync(condition, null, asNoTracking, cancellationToken);
        }

        public Task<TEntity> GetAsync(
            Expression<Func<TEntity, bool>> condition,
            Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> includes,
            CancellationToken cancellationToken = default)
        {
            return GetAsync(condition, includes, false, cancellationToken);
        }

        public async Task<TEntity> GetAsync(
            Expression<Func<TEntity, bool>> condition,
            Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> includes,
            bool asNoTracking,
            CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (condition != null)
            {
                query = query.Where(condition);
            }

            if (includes != null)
            {
                query = includes(query);
            }

            if (asNoTracking)
            {
                query = query.AsNoTracking();
            }

            return await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
        }

        public Task<TEntity> GetAsync(Specification<TEntity> specification, CancellationToken cancellationToken = default)
        {
            return GetAsync(specification, false, cancellationToken);
        }

        public async Task<TEntity> GetAsync(Specification<TEntity> specification, bool asNoTracking, CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (specification != null)
            {
                query = query.GetSpecifiedQuery(specification);
            }

            if (asNoTracking)
            {
                query = query.AsNoTracking();
            }

            return await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<TProjectedType> GetAsync<TProjectedType>(
            Expression<Func<TEntity, bool>> condition,
            Expression<Func<TEntity, TProjectedType>> selectExpression,
            CancellationToken cancellationToken = default)
        {
            if (selectExpression == null)
            {
                throw new ArgumentNullException(nameof(selectExpression));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (condition != null)
            {
                query = query.Where(condition);
            }

            return await query.Select(selectExpression).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<TProjectedType> GetAsync<TProjectedType>(
            Specification<TEntity> specification,
            Expression<Func<TEntity, TProjectedType>> selectExpression,
            CancellationToken cancellationToken = default)
        {
            if (selectExpression == null)
            {
                throw new ArgumentNullException(nameof(selectExpression));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (specification != null)
            {
                query = query.GetSpecifiedQuery(specification);
            }

            return await query.Select(selectExpression).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
        }

        public Task<bool> ExistsAsync(CancellationToken cancellationToken = default)
        {
            return ExistsAsync(null, cancellationToken);
        }

        public async Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> condition, CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (condition == null)
            {
                return await query.AnyAsync(cancellationToken);
            }

            bool isExists = await query.AnyAsync(condition, cancellationToken).ConfigureAwait(false);
            return isExists;
        }

        public async Task<object[]> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            EntityEntry<TEntity> entityEntry = await uowHander.GetActiveUnitOfWork().Set<TEntity>().AddAsync(entity, cancellationToken).ConfigureAwait(false);
            await uowHander.GetActiveUnitOfWork().SaveChangesAsync(cancellationToken).ConfigureAwait(false);

            object[] primaryKeyValue = entityEntry.Metadata.FindPrimaryKey().Properties.
                Select(p => entityEntry.Property(p.Name).CurrentValue).ToArray();

            return primaryKeyValue;
        }

        public async Task InsertAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            if (entities == null)
            {
                throw new ArgumentNullException(nameof(entities));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            await uowHander.GetActiveUnitOfWork().Set<TEntity>().AddRangeAsync(entities, cancellationToken).ConfigureAwait(false);
            await uowHander.GetActiveUnitOfWork().SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }        

        public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            uowHander.GetActiveUnitOfWork().Set<TEntity>().Remove(entity);
            await uowHander.GetActiveUnitOfWork().SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task DeleteAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            if (entities == null)
            {
                throw new ArgumentNullException(nameof(entities));
            }
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            uowHander.GetActiveUnitOfWork().Set<TEntity>().RemoveRange(entities);
            await uowHander.GetActiveUnitOfWork().SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<int> GetCountAsync(CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            int count = await uowHander.GetActiveUnitOfWork().Set<TEntity>().CountAsync(cancellationToken).ConfigureAwait(false);
            return count;
        }

        public async Task<int> GetCountAsync(Expression<Func<TEntity, bool>> condition, CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (condition != null)
            {
                query = query.Where(condition);
            }

            return await query.CountAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<int> GetCountAsync(IEnumerable<Expression<Func<TEntity, bool>>> conditions, CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (conditions != null)
            {
                foreach (Expression<Func<TEntity, bool>> expression in conditions)
                {
                    query = query.Where(expression);
                }
            }

            return await query.CountAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<long> GetLongCountAsync(CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            long count = await uowHander.GetActiveUnitOfWork().Set<TEntity>().LongCountAsync(cancellationToken).ConfigureAwait(false);
            return count;
        }

        public async Task<long> GetLongCountAsync(Expression<Func<TEntity, bool>> condition, CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (condition != null)
            {
                query = query.Where(condition);
            }

            return await query.LongCountAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<long> GetLongCountAsync(IEnumerable<Expression<Func<TEntity, bool>>> conditions, CancellationToken cancellationToken = default)
        {
            using var uowHander = _unitOfWorkManager.Begin<TDbContext>();
            IQueryable<TEntity> query = uowHander.GetActiveUnitOfWork().Set<TEntity>();

            if (conditions != null)
            {
                foreach (Expression<Func<TEntity, bool>> expression in conditions)
                {
                    query = query.Where(expression);
                }
            }

            return await query.LongCountAsync(cancellationToken).ConfigureAwait(false);
        }      
        /*****************************************************************************************************/
    }
}
